﻿using System.DrawingCore;
using System.DrawingCore.Imaging;
using MyFurion.Unility.ShareDto;
using MyFurion.Unility.EnumData;

namespace MyFurion.Unility.Generic
{
    /// <summary>
    /// 验证码
    /// </summary>
    public class CaptchaHelper:ITransient
    {
        private readonly RedisCacheHelper redisCacheHelper;
        private readonly MemoryCache _memoryCache;
        /// <summary>
        /// 构造参数
        /// </summary>
        /// <param name="redisCache"></param>
        /// <param name="memoryCache"></param>
        public CaptchaHelper(RedisCacheHelper redisCache, MemoryCache memoryCache)
        {
            redisCacheHelper=redisCache;
            _memoryCache=memoryCache;
        }
        /// <summary>
        /// 获取验证码
        /// </summary>
        /// <param name="captchaType">验证码类型 1：数字 2：数字加字母 3：数字运算</param>
        /// <param name="cacheType">验证码缓存类型 1：memory缓存 2：redis缓存 默认1</param>
        /// <param name="codeLength">验证码长度 默认4</param>
        /// <param name="cacheTime">缓存时长 单位秒 默认300秒</param>
        /// <returns></returns>
        public Task<CaptchaResult> CreateCaptcha(EnumCaptchaType captchaType, CacheType cacheType =CacheType.MEMORY, int codeLength=4, int cacheTime=300)
        {
            int codeW = 170;//宽度
            int codeH = 50;//高度
            int fontSize = 32;//字体大小
            //初始化验证码
            string charCode = string.Empty;
            string resultCode = "";
            switch (captchaType)
            {
                case EnumCaptchaType.NUM:
                    charCode = CreateNumCode(codeLength);
                    break;
                case EnumCaptchaType.ARITH:
                    charCode = CreateArithCode(out resultCode);
                    codeLength = charCode.Length;
                    break;
                default:
                    charCode = CreateCharCode(codeLength);
                    break;
            }
            //颜色列表
            Color[] colors = { Color.Black, Color.Red, Color.Blue, Color.Green, Color.Orange, Color.Brown, Color.DarkBlue };
            //字体列表
            string[] fonts = { "Times New Roman", "Verdana", "Arial", "Gungsuh" };
            //创建画布
            Bitmap bitmap = new Bitmap(codeW, codeH);
            Graphics graphics = Graphics.FromImage(bitmap);
            graphics.Clear(Color.White);
            Random random = new Random();
            //画躁线
            for (int i = 0; i < codeLength; i++)
            {
                int x1 = random.Next(codeW);
                int y1 = random.Next(codeH);
                int x2 = random.Next(codeW);
                int y2 = random.Next(codeH);
                Color color = colors[random.Next(colors.Length)];
                Pen pen = new Pen(color);
                graphics.DrawLine(pen, x1, y1, x2, y2);
            }
            //画噪点
            for (int i = 0; i < 100; i++)
            {
                int x = random.Next(codeW);
                int y = random.Next(codeH);
                Color color = colors[random.Next(colors.Length)];
                bitmap.SetPixel(x, y, color);
            }
            //画验证码
            for (int i = 0; i < codeLength; i++)
            {
                string fontStr = fonts[random.Next(fonts.Length)];
                Font font = new Font(fontStr, fontSize);
                Color color = colors[random.Next(colors.Length)];
                graphics.DrawString(charCode[i].ToString(), font, new SolidBrush(color), (float)i * 30 + 5, (float)0);
            }
            //写入内存流
            MemoryStream stream = new MemoryStream();
            bitmap.Save(stream, ImageFormat.Jpeg);
            CaptchaResult verifyCode = new CaptchaResult();
            verifyCode.CodeToken = Guid.NewGuid().ToString().Replace("-", "");
            verifyCode.ImageData = "data:image/jpeg;base64," + Convert.ToBase64String(stream.ToArray());
            //释放资源
            graphics.Dispose();
            bitmap.Dispose();
            //写入Redis缓存
            string cacheValue = captchaType == EnumCaptchaType.ARITH ? resultCode : charCode;
            if (cacheType == CacheType.REDIS)
            {
                redisCacheHelper.SetRedisCache(verifyCode.CodeToken, cacheValue, cacheTime);
            }
            else
            {
                _memoryCache.SetCache(verifyCode.CodeToken, cacheValue, cacheTime);
            }      
            return Task.FromResult(verifyCode);
        }
        /// <summary>
        /// 验证码校验
        /// </summary>
        /// <param name="input">验证码信息</param>
        /// <param name="cacheType">缓存类别</param>
        /// <returns></returns>
        public Task<string> CheckCaptcha(CaptchaInput input,CacheType cacheType)
        {
            string checkResult = "";
            string codeToken = input.CodeToken;
            string captchaData = input.CaptchaData;
            string rightCode="";
            if (cacheType == CacheType.REDIS)
            {
                rightCode = redisCacheHelper.GetReidsCache(codeToken);
            }
            else
            {
                rightCode=_memoryCache.GetCache(codeToken);
            }
            if (string.IsNullOrWhiteSpace(rightCode))
            {
                checkResult = "验证码已失效";
            }
            else
            {
                if (string.Compare(rightCode, captchaData, true) != 0)
                {
                    checkResult = "验证码错误";
                }
                else
                {
                    //删除缓存
                    if (cacheType == CacheType.REDIS)
                    {
                        redisCacheHelper.DelRedisCache(codeToken);
                    }
                    else
                    {
                        _memoryCache.DelCache(codeToken);
                    }       
                }
            }
            return Task.FromResult(checkResult);
        }
        /// <summary>
        /// 获取数字验证码
        /// </summary>
        /// <param name="n">验证码数</param>
        /// <returns></returns>
        public static string CreateNumCode(int n)
        {
            char[] numChar = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
            string charCode = string.Empty;
            Random random = new Random();
            for (int i = 0; i < n; i++)
            {
                charCode += numChar[random.Next(numChar.Length)];
            }
            return charCode;
        }
        /// <summary>
        /// 获取字符验证码
        /// </summary>
        /// <param name="n">验证码数</param>
        /// <returns></returns>
        public static string CreateCharCode(int n)
        {
            char[] strChar = { 'a', 'b','c','d','e','f','g','h','i','j','k','l','m',
                'n','o','p','q','r','s','t','u','v','w','x','y','z','0','1','2','3',
                '4','5','6','7','8','9','A','B','C','D','E','F','G','H','I','J','K',
                'L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'};

            string charCode = string.Empty;
            Random random = new Random();
            for (int i = 0; i < n; i++)
            {
                charCode += strChar[random.Next(strChar.Length)];
            }
            return charCode;
        }
        /// <summary>
        /// 获取运算符验证码
        /// </summary>
        /// <returns></returns>
        public static string CreateArithCode(out string resultCode)
        {
            string checkCode = "";
            Random random = new Random();
            int intFirst = random.Next(1, 20);//生成第一个数字
            int intSec = random.Next(1, 20);//生成第二个数字
            int intTemp = 0;
            switch (random.Next(1, 3).ToString())
            {
                case "2":
                    if (intFirst < intSec)
                    {
                        intTemp = intFirst;
                        intFirst = intSec;
                        intSec = intTemp;
                    }
                    checkCode = intFirst + "-" + intSec + "=";
                    resultCode = (intFirst - intSec).ToString();
                    break;
                default:
                    checkCode = intFirst + "+" + intSec + "=";
                    resultCode = (intFirst + intSec).ToString();
                    break;
            }
            return checkCode;
        }
    }
}
